perm filename PRIMER.SAI[REV,MUS] blob sn#290446 filedate 1977-06-25 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00007 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002	ENTRY incr_prime
C00003 00003	∂ Declarations for SIEVE.
C00004 00004	INTERNAL INTEGER PROCEDURE incr_prime(
C00007 00005	    ∂ Go looking for primes.
C00011 00006	    ∂ Got all the primes needed, or else ran out of table.
C00012 00007	END "PRIMER".
C00013 ENDMK
C⊗;
ENTRY incr_prime;
BEGIN "PRIMER"
REQUIRE "HEADER.SAI[REV,KS]" SOURCE_FILE;

∂ Ken Shoemake.  June 1977.
This module finds the k'th prime above or below a given integer, or the nearest
prime if k is zero.
;
∂ Declarations for SIEVE.;
∂ These things currently found in PSIEVE.SAI[REV,KS];
EXTERNAL INTEGER #SIEVE;
EXTERNAL INTEGER ARRAY SIEVE[0:(#SIEVE DIV 36)]; ∂ Bounds are ignored.;

INTERNAL INTEGER PROCEDURE incr_prime(
	INTEGER n, incr(0));
∂ Increments or decrements n by incr prime numbers.  If incr is 0, returns
nearest prime to n.  If n is outside the bounds of the prime table, it is
returned as the result, and an error message is printed.  Otherwise the
result is guaranteed to be prime.  An error message will be printed if the
attempt to increment/decrement n exceeds the range of the table, and the
maximum/minimum prime in the table will be returned as the result.  The
table is stored as a bit vector, 36 bits to a word, where a one bit means
that index is prime.  Both the bit vector (SIEVE) and the maximum prime
in it (#SIEVE) are defined EXTERNALly in PSIEVE.REL, which is produced from
PSIEVE.SAI, which is generated by executing PSGEN.SAI.  Counting the high
order bit in a word as bit 0 and the low order bit as bit 35, the bit
corresponding to a given integer n will be bit (n MOD 36) in SIEVE[(n DIV 36)].
;
    BEGIN "incr prime"
    INTEGER wrd, bit, dir;

    IF NOT (2 ≤ n ≤ #SIEVE)
      THEN BEGIN
	PRINT(↓,"incr_prime: 2 ≤ n ≤ ",#SIEVE,↓);
	RETURN(n);
	END;

    wrd ← n DIV 36;
    bit ← n MOD 36;

    IF incr = 0
      THEN BEGIN "nearest prime"
	INTEGER top, bot;
	IF (SIEVE[wrd] LSH bit) < 0	∂ Make n's bit the sign bit and test it;
	  THEN RETURN(n);
	top ← incr_prime(n,1);
	bot ← incr_prime(n,-1);
	IF ((top+bot) DIV 2) ≥ n	∂ Choose the nearer prime, lower if tie;
	  THEN RETURN(bot)
	  ELSE RETURN(top);
	END "nearest prime";

    ∂ Go looking for primes.;
    IF incr < 0
      THEN BEGIN
	dir ← -1;
	incr ← -incr;
	END
      ELSE BEGIN
	dir ← 0;
	wrd ← wrd LOR ((wrd-((#SIEVE DIV 36)+1)) LSH 18);
	END;


	START_CODE
	DEFINE inc=0, tp=1, ac=2, bt=ac+1, wd=4, dr=5, sv=6;
	LABEL Xcrement,Decr,Mask,Nope,GotOne,Pinpoint,GotIt,IncrWd,Lose,TheEnd;

		MOVEI	sv,ACCESS(SIEVE[0]);	∂ Need to have this address;
		TLO	sv,wd;		∂ Will be indexed by wd;
		MOVE	inc,incr;	∂ These should come →after← ACCESS ;
		MOVE	bt,bit;		∂   to avoid getting clobbered;
		MOVE	wd,wrd;
		MOVE	dr,dir;

	    Xcrement:MOVN    tp,bt;	∂ Go up/down looking for another prime;
		    JUMPL   dr,Decr;	∂ Mask off bits behind current position;
		    SETO    ac,;
		    LSH     ac,-1(tp);	∂ Sets ac←00...0x1...11, x=current,set to 0;
		    JRST    Mask;
	    Decr:   MOVSI   ac,'400000;
		    ASH     ac,1(tp);	∂ Sets ac←11...1x0...00, x=current,set to 0;
	    Mask:   AND     ac,@sv;	∂ Address is effectively SIEVE[wd];
		    JUMPN   ac,GotOne;	∂ Is there a prime in the same word?;

		Nope:   XCT     IncrWd(dr);	∂ No primes yet, try next word;
			SKIPN   ac,@sv;	∂ Any primes in it (SIEVE[wd]) ?;
			JRST    Nope;

	    GotOne: JUMPGE  dr,Pinpoint;∂ Got a prime, but exactly what bit is it?;
		    MOVN    tp,ac;	∂ Get the proper one for this direction;
		    ANDM    tp,ac;	∂ Pick out lowest one bit, else highest;
	    Pinpoint:JFFO   ac,GotIt;	∂ Put the bit number in bt (=ac+1);
	    GotIt:  SOJG    inc,Xcrement;	∂ Go back for more, unless sated;

		HRRZM	wd,wrd;		∂ Return word and bit where found prime;
		MOVEM	bt,bit;
		MOVEM	inc,incr;	∂ Will be zero, indicating total success;
		JRST	TheEnd;

	∂ The following instructions are not executed in-line, but by XCT at Nope;
		SOJL    wd,Lose;	∂ Decrement and test against 0;
	IncrWd: AOBJP   wd,Lose;	∂ Increment and test against #SIEVE/36;

	Lose:	HRRZM	wd,wrd;		∂ Return word where lost;
		MOVEM	inc,incr;	∂ Will be non-zero, indicating failure;

	TheEnd:
	END;
    ∂ Got all the primes needed, or else ran out of table.;
    ∂ Number of primes left to find will be in incr.;

    IF incr = 0
      THEN RETURN(bit+36*wrd)
      ELSE BEGIN
	PRINT(↓,"incr_prime: 2 ≤ n ≤ ",#SIEVE,↓);
	IF dir = -1		∂ If going down, then minimum prime, else maximum;
	  THEN RETURN(2)
	  ELSE RETURN(#SIEVE);	∂ #SIEVE is the largest prime in table. Sorry;
	END;

END "incr prime";
END "PRIMER".